home *** CD-ROM | disk | FTP | other *** search
- (c) Copyright 1992 Commodore-Amiga, Inc. All rights reserved.
- The information contained herein is subject to change without notice,
- and is provided "as is" without warranty of any kind, either expressed
- or implied. The entire risk as to the use of this information is
- assumed by the user.
-
- Packet Level I/O under Release 2
-
-
- by Dale Larson and John Orr
-
-
- Normally when an application needs to perform I/O using DOS, it uses
- functions from the dos.library to open, read, write, and close any
- I/O channels. These functions take care of talking to the underlying
- DOS device (see the ``About Devices'' section below), shielding the
- programmer from most of the grunt work it takes to carry out the I/O.
- These functions are adequate for most I/O needs.
-
- *********************************************************
- * *
- * About Devices *
- * *
- * *
- * The term ``DOS devices'' is confusing because the *
- * term ``device'' is a bit overloaded. There are *
- * three entities on the Amiga that are referred to as *
- * a device. There is the Exec device (for example *
- * trackdisk.device and console.device) the AmigaDOS *
- * device (such as DF0: and RAM:), and the logical *
- * AmigaDOS device name (for example, SYS:, C:, and *
- * S:). The Exec level device is basically a subset of *
- * an Exec library. Exec devices are described in the *
- * RKRM: Devices book. The AmigaDOS device is a higher *
- * level entity than the Exec device (AmigaDOS devices *
- * often utilize Exec devices). An AmigaDOS device has *
- * a process associated with it, normally referred to *
- * as a handler, that controls the AmigaDOS I/O stream *
- * via a FileHandle. The AmigaDOS logical device name *
- * (better known as an ``assign'') is made to look like *
- * an AmigaDOS device, but is a higher level entity *
- * than the AmigaDOS device. Both the AmigaDOS device *
- * and the logical device name are referred to by a *
- * name ending in a colon (':'). The logical device *
- * can refer to any directory on an AmigaDOS device (as *
- * long as the AmigaDOS device supports having files). *
- * The user can change the directory to which logical *
- * device name refers from a command line. This article *
- * deals primarily with AmigaDOS devices. *
- * *
- *********************************************************
-
- One of the reasons these functions are only adequate is because they
- are synchronous. When an application attempts to transfer data using
- one of these functions, the I/O function waits for the entire
- transfer to finish before returning. Ideally, the application should
- be able to perform I/O asynchronously, so it can do something else
- while the data transfer takes place.
-
- Another reason that these functions are only adequate is because they
- don't implement all of the features built into DOS devices. To
- utilize these features, an application has to work on a lower level.
- The application has to talk directly to the DOS device.
-
-
- The Packet Level
-
- When DOS functions talk to devices such as the floppy drive or the
- serial port, they talk to a special process called a handler. Every
- DOS device (like CON:, SER:, DF0:, and PIPE:) has a handler process.
- The handler process is responsible for receiving and carrying out a
- standard set of DOS device commands. It provides a standard
- programming interface to a lower-level I/O software or hardware
- entity (such as an Exec device). The packet interface abstracts the
- lower-level entity so that dos.library functions don't have to deal
- with a lot of bothersome details such as moving a disk head or
- reading serial registers. The interface to every handler is the
- same, so every DOS device operates in the same manner, regardless of
- any underlying software or hardware. Theoretically, to the
- dos.library functions, writing to the console handler (CON:) should
- be no different than writing to the serial port handler (SER:) or the
- DF0: handler. The handler lets dos.library functions treat all DOS
- devices in the same way.
-
- Typically, the handler talks directly to an underlying Exec device.
- Two examples are the CON: and the DF0: handlers. The CON: handler
- process talks directly to the console.device. When trying to access
- a floppy in DF0:, the DF0: handler talks directly to the
- trackdisk.device. These handlers accept handler level commands (for
- example, ACTION_READ and ACTION_WRITE) and hide any Exec level I/O
- from the dos.library functions and subsequently, the application.
-
- In cases like the DF0: handler, which is a special type of handler
- called a file system, the handler has to take care of organizing the
- lower-level medium into directories and files. The DF0: handler
- takes care of translating directory and file names into terms the
- trackdisk.device can understand (disk blocks). The only requirement
- of a handler to be a file system is that the handler must support
- files. A file system does not have to support a directory structure
- to be considered a file system. Handlers which are not file systems
- are called stream handlers.
-
- Some handlers do not have any underlying software or hardware.
- Handlers such as RAM: have no underlying software or hardware. These
- kinds of handlers take care of implementing everything necessary to
- carry out the I/O rather than delegating to an Exec device.
-
- Handlers receive commands through an Exec message port. Every
- handler process has a message port for receiving commands. A handler
- accepts a command in the form of a DosPacket structure (defined in
- <dos/dosextens.h>), which is an extension of an Exec Message
- structure:
-
- struct DosPacket {
- struct Message *dp_Link;
- struct MsgPort *dp_Port; /* Reply port for the packet */
- /* Must be filled in each send. */
- LONG dp_Type;
- LONG dp_Res1; /* Result #1 */
- LONG dp_Res2; /* For file system calls this is what would
- * have been returned by IoErr() */
- LONG dp_Arg1; /* Argument list */
- LONG dp_Arg2;
- LONG dp_Arg3;
- LONG dp_Arg4;
- LONG dp_Arg5;
- LONG dp_Arg6;
- LONG dp_Arg7;
- }; /* DosPacket */
-
- The dp_Type field contains an identifier corresponding to the
- command. The identifiers for each of the standard commands are
- defined in <dos/dosextens.h>. For example, the command to write data
- is ACTION_WRITE. Each packet type has different parameters, which
- the programmer supplies in the ``dp_Arg'' fields.
-
- After a handler finishes with a packet, it returns the packet to the
- message port in dp_Port. The handler places return values (including
- any error codes) in the result fields dp_Res1 and dp_Res2. If there
- was an error, dp_Res2 contains the corresponding DOS error code.
-
- The packet types are described in the Amiga Mail article ``AmigaDOS
- Packet Interface Specification'' on page II-5 and also in The
- AmigaDOS Manual, 3rd Edition. Since its original publication, the
- ``AmigaDOS Packet Interface Specification'' has been updated numerous
- times in Amiga Mail to correct errors. The information in that
- article (plus its errata) should appear in the next edition of The
- AmigaDOS Manual.
-
-
- Finding the Handler
-
- There are various ways to find the address of the target handler's
- Message port, which is also called a process identifier by some
- documentation. When working with an open FileHandle, the handler's
- port address is in the FileHandle's fh_Type field. Note that the
- dos.library functions normally access a FileHandle using a BPTR, so
- to get to the fh_Type field an application has to do something like
- this:
-
- my_handler_port = ((struct FileHandle *)BADDR(FileHandle))->fh_Type);
-
- Because the AmigaDOS device NIL: has no handler process, the fh_Type
- field of any of its file handle's will be NULL. The application must
- account for this case.
-
- When working with a device or assign name, an application can get to
- the handler's message port by using the dos.library function
- GetDeviceProc():
-
- struct DevProc *GetDeviceProc(UBYTE *dev_name, struct DevProc *prev_devproc);
-
- This function returns a pointer to the following structure:
-
- struct DevProc {
- struct MsgPort *dvp_Port; /* The handler's Message port */
- BPTR dvp_Lock; /* (send packets there) */
- ULONG dvp_Flags;
- struct DosList *dvp_DevNode; /* DON'T TOUCH OR USE! */
- };
-
- This function is intended to improve on the DeviceProc() function as
- it also deals with multiple assigns. GetDeviceProc() must be
- countered by FreeDeviceProc(). See the GetDeviceProc() Autodoc for
- more details.
-
-
- The Old Way
-
- Prior to Release 2, sending packets was relatively complicated. Some
- of the early disks from the Fred Fish Library (disks 35, 56, and 66)
- contain complete examples of using DOS packets synchronously and
- asynchronously. As of Release 2.04, dos.library contains functions
- which make it easier to use DOS packets synchronously and
- asynchronously.
-
-
- Synchronous Packet Calls
-
- Performing synchronous packet I/O is now very simple. Simply call
- the dos.library function DoPkt():
-
- LONG DoPkt(struct MsgPort *handler_port, LONG action_id, LONG arg1,
- LONG arg2, LONG arg3, LONG arg4, LONG arg5);
-
- This function allocates and fills out a DosPacket using the packet
- type ('action_id') and arguments ('arg1', 'arg2', etc.) you supply.
- This function then sends off the packet to the 'handler_port' and
- waits for the packet to return. DoPkt() returns the value from the
- packet's dp_Res1 field. To get the value from the packet's dp_Res2
- field, call the dos.library function IoErr(). Assembly language
- programmers can also get dp_Res2 from register D1.
-
-
- Asynchronous Packet Calls
-
- To do asynchronous packet calls, things are a little more complex,
- but they are still much better than they were before V37 (This
- subject was partially covered in Martin Taillefer's article, ``Fast
- AmigaDOS I/O'', page II-77, from the September/October 1992 issue of
- Amiga Mail). When using a DOS packet asynchronously, there are three
- things to do before sending the packet anywhere:
-
- o Acquire a message port
- o Allocate and initialize the packet
- o Set up the packet's arguments
-
-
- Acquiring the Message Port
-
- It is possible for an application to use its process message port for
- packet transmissions rather than allocating a new one. It can get to
- its Process structure by calling FindTask() with an argument of NULL.
- The Process structure has an Exec MsgPort structure embedded in it,
- which an application can get to via the pr_MsgPort field. The bad
- thing about using this port is that many system functions also use
- it. Consequently, after sending an asynchronous packet, an
- application can not call any system functions that use pr_MsgPort.
- This includes most dos.library functions, many C compiler linker
- library functions, and many standard I/O functions (i.e., printf()
- and scanf()). The application has to wait for the asynchronous
- packet to return before calling such functions. If the application
- sent an asynchronous packet and inadvertently initiated some other
- packet level I/O before the asynchronous packet came back, it is
- possible to cause an ``unexpected packet received'' system crash if
- the asynchronous packet returned at the wrong time. Also, an
- application should remove such a packet from the process message port
- using the WaitPkt() function. This function will take care of any
- system idiosyncrasies that the application would otherwise need to
- address itself. This applies to idiosyncrasies that exist now or new
- ones that may appear in the future.
-
- As of V37, arguably the best and easiest way to acquire a message
- port is to create one with the exec.library call CreateMsgPort(). By
- allocating its own message port, the application doesn't have to
- worry about any problems with sharing the port.
-
-
- Allocating and Initializing a Packet
-
- The packet I/O system uses Exec's message passing system, so each
- DosPacket must also have an Exec Message structure. As both
- structures are necessary, they have been combined in a single
- StandardPacket structure (defined in <dos/dosextens.h>):
-
- struct StandardPacket {
- struct Message sp_Msg;
- struct DosPacket sp_Pkt;
- }; /* StandardPacket */
-
- To make a packet usable, the Exec Message and DosPacket structures
- have to be set up to point to each other. The DosPacket structure is
- straightforward about its link to its corresponding message. The
- DosPacket's dp_Link field must point to the packet's Message
- structure. However, the link from the Message to the DosPacket is a
- bit obscure. The Message contains an Exec Node structure which in
- turn contains a field called ln_Name. Although the Node structure
- defines ln_Name as a character array, the DOS packet I/O system
- instead requires that this field point to the Message's corresponding
- DosPacket:
-
- my_standard_pkt->sp_Msg.mn_Node.ln_Name = (STRPTR)(my_standard_pkt->sp_Pkt);
-
- As of V37, the dos.library function AllocDosObject() is usually the
- best way to allocate a StandardPacket. To allocate one, call:
-
- mypacket = AllocDosObject(DOS_STDPKT, NULL);
-
- This function takes care of linking the StandardPacket's Message and
- DosPacket. Note that the function call above does not return a
- pointer to a StandardPacket. This function allocates an entire
- StandardPacket structure, but it returns a pointer to the
- StandardPacket's sp_Pkt field. To access the sp_Msg portion of the
- StandardPacket, use the pointer in the dp_Link field.
-
- Any structure allocated with AllocDosObject() must be freed with
- FreeDosObject(). To free 'mypacket' from the above AllocDosObject()
- call:
-
- FreeDosObject(DOS_STDPKT, mypacket);
-
-
- Filling in Packet Arguments
-
- Before sending an asynchronous packet, an application needs to fill
- in its action and arguments. For example, a packet set up to read
- 4096 bytes from an open file handle might look something like this:
-
- . . .
-
- #define BUFSIZE 4096
-
- . . .
-
- BPTR myfh;
- struct DosPacket *my_pkt;
- UBYTE *buffer[BUFSIZE];
-
- . . .
-
- my_pkt->dp_Type = ACTION_READ;
- my_pkt->dp_Arg1 = ((struct FileHandle *)BADDR(myfh))->fh_Arg1;
- my_pkt->dp_Arg2 = buffer;
- my_pkt->dp_Arg3 = BUFSIZE;
-
- . . .
-
-
- Sending the Asynchronous Packet
-
- To send a packet asynchronously, use the dos.library SendPkt() function:
-
- SendPkt(struct DosPacket *mypacket,
- struct MsgPort *handlerport, struct MsgPort *replyport)
-
- This function sends 'mypacket' to 'handlerport' and exits without
- waiting for the packet to come back. When the packet returns, it
- will arrive at 'replyport'.
-
-
- Waiting for the Asynchronous Packet
-
- After calling SendPkt(), the application can go about its business,
- taking care of some other work while the DOS device handles the
- application's packet. Eventually, the application will have to test
- the reply port to see if the packet has come back yet. It can do
- this with WaitPort() or, if the application has to test for more than
- just the reply port's signal, it can use Wait(). If the application
- doesn't poll its signals too often, it can test its own signal bits
- using SetSignal() (see the exec.library Autodocs and the
- ``Introduction to Exec'' chapter of the Release 2 RKRM: Devices for
- more information on how to use these functions). Be sure to remove
- any and all packets from the message port using GetMsg().
-
- If the application used its process message port (pr_MsgPort) as the
- reply port, it must use the WaitPkt() function to remove the packet
- from the message port. As mentioned earlier, this function will take
- care of any hidden system idiosyncrasies so the application never has
- to account for them. WaitPkt() will not necessarily clear the
- process message port's signal bit. Like SendPkt(), WaitPkt() assumes
- any DosPacket it works with is part of a StandardPacket structure.
-
- The dos.library contains a function called AbortPkt() that, at a
- glance, looks like it might be useful to an application that needs to
- abort a packet (for example, upon receiving a break signal). This
- function, which was introduced in Release 2, is supposed to attempt
- to abort a packet that has already been sent to a handler. If the
- abort operation is successful, the aborted packet will arrive at its
- original reply port (the same place the packet would have arrived if
- it was successful). Unfortunately, the abort operation will never be
- successful, at least not under existing releases of the operating
- system. Currently, this function returns without doing anything (the
- most recent release is 3.0 or V39). In the future when AbortPkt()
- does do something, it will assume that any DosPacket it works with is
- part of a StandardPacket structure.
-
-
- Interpreting the Packet Results
-
- Upon returning to the application, for most packets, there will be
- result values in dp_Res1 and dp_Res2. For most packets, if dp_Res1
- is DOSTRUE, the packet returned without a problem. If dp_Res1 is
- DOSFALSE, the handler experienced an error with the packet and there
- should be an error code in dp_Res2. The error codes are defined in
- <dos/dos.h>. Note that not all packets follow this error reporting
- convention. See the ``AmigaDOS Packet Interface Specification''
- article on page II-5 for more information on how individual packets
- work.
-
-
- Packets without dos.library Functions
-
- Besides offering the ability to do asynchronous I/O, directly using
- DOS packets also allows applications to utilize features of certain
- handlers that are not available through a dos.library function.
-
- The following packets are not available via a dos.library function as
- of Release 3.0:
-
- ACTION_WRITE_PROTECT ACTION_DISK_INFO (for console handlers)
-
- If an application needs the feature that these packets provide, the
- application has to send the packet to the handler. The first two
- packets are fairly straightforward and are explained in the
- ``AmigaDOS Packet Interface Specification'' article on page II-5 of
- Amiga Mail.
-
- The ACTION_DISK_INFO packet is peculiar because its function changes
- depending on the handler. When sent to a file system handler, it
- returns information about its media. The dos.library function Info()
- uses this packet. The ACTION_DISK_INFO packet has a different
- meaning to a console handler. When a console handler receives this
- packet, it returns a pointer to the window of the open console file
- handle. When an application needs this pointer, the only way to get
- it as of Release 3.0 is to send the console handler this packet.
-
- There are two commonly used packets that an application could not
- call through a dos.library function under 1.3 that now have
- corresponding dos.library functions. The packet to rename a disk,
- ACTION_RENAME_DISK, is now available through the dos.library function
- Relabel(). Also the ACTION_SCREEN_MODE packet acquired a dos.library
- function, SetMode().
-
-
- About the Examples
-
- Two examples accompany this article. The first, CompareIO.c, uses
- packet level I/O to copy the standard input channel to the standard
- output channel (as set up by the standard startup code, c.o).
- CompareIO uses both synchronous and asynchronous I/O to perform the
- copy and reports the time it takes to do each. The other example,
- InOutCTRL-C.c, also uses packets to copy the standard input channel
- to the standard output channel, but it only uses asynchronous I/O.
- The second example does a better job checking for a user break.
-
-